package ui

import (
	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/canvas"
	"fyne.io/fyne/v2/driver/desktop"
	"fyne.io/fyne/v2/theme"
	"fyne.io/fyne/v2/widget"
	"go-scrcpy/internal/source"
	"go-scrcpy/pkg"
	"go-scrcpy/pkg/log"
	"image"
	"sync"
)

type TextureCard struct {
	widget.BaseWidget
	Image         *canvas.Image
	pix           *image.NRGBA
	adbSource     source.Source
	mouseEventSet mouseEventSet
	keyState      map[int]*int
	wg            sync.WaitGroup
	eventChan     chan singleMouseEvent
	close         bool
}

func NewTextureCard(width, height int, adbSource source.Source) *TextureCard {
	card := &TextureCard{
		adbSource: adbSource,
		eventChan: make(chan singleMouseEvent, 10),
	}

	card.pix = image.NewNRGBA(image.Rect(0, 0, width, height))
	card.Image = canvas.NewImageFromImage(card.pix)
	card.Image.FillMode = canvas.ImageFillOriginal
	card.Image.ScaleMode = canvas.ImageScaleFastest
	card.ExtendBaseWidget(card)
	card.keyState = make(map[int]*int)
	card.wg.Add(1)
	go func() {
		defer card.wg.Done()
		for event := range card.eventChan {
			card.mouseEventSet.accept(event)
			if err := card.mouseEventSet.Serialize(card.adbSource); err != nil {
				log.Error("%v", err)
			}
		}
	}()
	return card
}

func (t *TextureCard) CreateRenderer() fyne.WidgetRenderer {
	t.ExtendBaseWidget(t)
	r := &textureRenderer{
		image: t.Image,
	}
	r.SetObjects([]fyne.CanvasObject{t.Image})
	return r
}

func (t *TextureCard) Pix() *image.NRGBA {
	return t.pix
}

func (t *TextureCard) MouseDown(e *desktop.MouseEvent) {
	if t.keyState[mainPointerKeyCode] != nil {
		log.Error("main pointer state error")
	}

	t.keyState[mainPointerKeyCode] = fingers.GetId()
	log.Info("MouseDown  event = %+v", e)
	t.eventChan <- singleMouseEvent{
		touchPoint: touchPoint{
			Point: pkg.Point{X: int(e.Position.X) * 3, Y: int(e.Position.Y) * 3},
			id:    *t.keyState[mainPointerKeyCode],
		},
		action: AMOTION_EVENT_ACTION_DOWN,
	}
}
func (t *TextureCard) MouseUp(e *desktop.MouseEvent) {
	if t.keyState[mainPointerKeyCode] == nil {
		return
	}
	t.eventChan <- singleMouseEvent{
		touchPoint: touchPoint{
			Point: pkg.Point{X: int(e.Position.X) * 3, Y: int(e.Position.Y) * 3},
			id:    *t.keyState[mainPointerKeyCode],
		},
		action: AMOTION_EVENT_ACTION_UP,
	}
	fingers.Recycle(t.keyState[mainPointerKeyCode])
	t.keyState[mainPointerKeyCode] = nil
}

// MouseIn is a hook that is called if the mouse pointer enters the element.
func (t *TextureCard) MouseIn(*desktop.MouseEvent) {

}

// MouseMoved is a hook that is called if the mouse pointer moved over the element.
func (t *TextureCard) MouseMoved(e *desktop.MouseEvent) {
	if t.keyState[mainPointerKeyCode] == nil {
		return
	}
	t.eventChan <- singleMouseEvent{
		touchPoint: touchPoint{
			Point: pkg.Point{X: int(e.Position.X) * 3, Y: int(e.Position.Y) * 3},
			id:    *t.keyState[mainPointerKeyCode],
		},
		action: AMOTION_EVENT_ACTION_MOVE,
	}
}

// MouseOut is a hook that is called if the mouse pointer leaves the element.
func (t *TextureCard) MouseOut() {
	if t.keyState[mainPointerKeyCode] == nil {
		return
	}
	t.eventChan <- singleMouseEvent{
		touchPoint: touchPoint{
			Point: pkg.Point{X: 0, Y: 0},
			id:    *t.keyState[mainPointerKeyCode],
		},
		action: AMOTION_EVENT_ACTION_UP,
	}
	fingers.Recycle(t.keyState[mainPointerKeyCode])
	t.keyState[mainPointerKeyCode] = nil

}

func (t *TextureCard) Stop() {
	if t.close {
		return
	}
	close(t.eventChan)
	t.wg.Wait()
	t.close = true
}

type textureRenderer struct {
	objects []fyne.CanvasObject
	image   *canvas.Image
}

func (i *textureRenderer) MinSize() fyne.Size {
	size := theme.IconInlineSize()
	return fyne.NewSize(size, size)
}

func (i *textureRenderer) Layout(size fyne.Size) {
	if len(i.Objects()) == 0 {
		return
	}

	i.Objects()[0].Resize(size)
}

func (i *textureRenderer) Refresh() {
	canvas.Refresh(i.image)
}
func (i *textureRenderer) Destroy() {
}

// Objects returns the objects that should be rendered.
//
func (i *textureRenderer) Objects() []fyne.CanvasObject {
	return i.objects
}
func (i *textureRenderer) SetObjects(objects []fyne.CanvasObject) {
	i.objects = objects
}
